package com.yifeng.repo.apm.endpoint.logging.logback;

import ch.qos.logback.classic.LoggerContext;
import ch.qos.logback.classic.selector.ContextSelector;
import ch.qos.logback.classic.spi.ILoggingEvent;
import ch.qos.logback.core.Appender;
import ch.qos.logback.core.FileAppender;
import com.yifeng.repo.apm.endpoint.console.api.model.LogConfTypeEnum;
import com.yifeng.repo.apm.endpoint.sender.LogSenderManager;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Iterator;
import java.util.List;

/**
 * Created by daibing on 2021/2/19.
 * -Dlogback.ContextSelector=com.yifeng.repo.apm.endpoint.logging.logback.TraceContextSelector
 * -Dorg.springframework.boot.logging.LoggingSystem=none
 * 必须同时设置这2个启动参数才可以生效，如果不设置第二个参数就会造成注入的appender在启动过程中就stop了
 */
public class TraceContextSelector implements ContextSelector {

    private LoggerContext traceLoggerContext;

    public TraceContextSelector(LoggerContext context) {
        this.traceLoggerContext = context;
        this.addTraceAppender(this.traceLoggerContext);

        boolean fileLogEnabled = LogSenderManager.getLogSender(true).isLogEnabled(LogConfTypeEnum.FILE);
        if (!fileLogEnabled) {
            this.removeFileAppender(this.traceLoggerContext);
        }
    }

    @Override
    public LoggerContext getLoggerContext() {
        return this.getDefaultLoggerContext();
    }

    @Override
    public LoggerContext getLoggerContext(String name) {
        if (traceLoggerContext.getName().equals(name)) {
            return traceLoggerContext;
        } else {
            return null;
        }
    }

    @Override
    public LoggerContext getDefaultLoggerContext() {
        return traceLoggerContext;
    }

    @Override
    public LoggerContext detachLoggerContext(String loggerContextName) {
        return traceLoggerContext;
    }

    @Override
    public List<String> getContextNames() {
        return Arrays.asList(traceLoggerContext.getName());
    }

    private void removeFileAppender(LoggerContext traceLoggerContext) {
        Iterator<Appender<ILoggingEvent>> it = traceLoggerContext.getLogger(LogConfTypeEnum.ROOT.name()).iteratorForAppenders();
        List<Appender<ILoggingEvent>> appenderList = new ArrayList<>();
        while (it.hasNext()) {
            Appender<ILoggingEvent> appender = it.next();
            if (appender instanceof FileAppender) {
                appender.stop();
                appenderList.add(appender);
            }
        }
        for (Appender<ILoggingEvent> appender : appenderList) {
            System.err.println("remove appender: " + appender.getName());
            traceLoggerContext.getLogger(LogConfTypeEnum.ROOT.name()).detachAppender(appender);
        }
    }

    private void addTraceAppender(LoggerContext traceLoggerContext) {
        TraceLogbackAppender traceLogbackAppender = new TraceLogbackAppender();
        traceLogbackAppender.setName(LogConfTypeEnum.CLOUD.name());
        traceLogbackAppender.setContext(traceLoggerContext);
        traceLogbackAppender.start();
        traceLoggerContext.getLogger(LogConfTypeEnum.ROOT.name()).addAppender(traceLogbackAppender);
    }
}
